
////////////////////////////////////////////////////////
//Prefs

if($Pref::Server::RedAutosave::SaveFile $= "") {
	$Pref::Server::RedAutosave::SaveFile       = "Autosave_Latest";
	$Pref::Server::RedAutosave::BackupDir      = "AutosaveBackups";
	$Pref::Server::RedAutosave::BackupFile     = "Autosave_%T";
	$Pref::Server::RedAutosave::BackupInterval = 5;
	$Pref::Server::RedAutosave::SaveTime       = 10;
	$Pref::Server::RedAutosave::SaveOwnership  = 1;
	$Pref::Server::RedAutosave::SaveEvents     = 1;
	$Pref::Server::RedAutosave::Running        = false;
	$Pref::Server::RedAutosave::LoadOnStart    = false;
}

////////////////////////////////////////////////////////
//Timing

exec("./timebomb.cs");

if(!isObject(RedAutosave_Bomb)) {
	new ScriptObject(RedAutosave_Bomb) {
		class = "timeBomb";
		tickMinutes = 1;
		requiredTicks = $Pref::Server::RedAutosave::SaveTime;
		go = false;
	};
	
	$RedAutosave::Running = false;
	$RedAutosave::SavesSinceLastBackup = 1000;
}

function RedAutosave_Bomb::onTick(%this, %time) {
	RedAutosave_Autosave();
}

////////////////////////////////////////////////////////
//Support

function RedAutosave_GetBrickCount() {
	%bricks = 0;
	
	for(%i=0;%i<mainBrickGroup.getCount();%i++) {
		%bricks += mainBrickGroup.getObject(%i).getCount();
	}
	
	return %bricks;
}

function RedAutosave_GetCorrectTimeString() {
	%timestr = getDateTime();
	
	%mh = getSubStr(%timestr,  0, 2);
	%dy = getSubStr(%timestr,  3, 2);
	%yr = getSubStr(%timestr,  6, 2);
	%hr = getSubStr(%timestr,  9, 2);
	%me = getSubStr(%timestr, 12, 2);
	%sd = getSubStr(%timestr, 15, 2);
	
	%correcttimestr = %yr @ "_" @ %mh @ "_" @ %dy @ "_" @ %hr @ "_" @ %me @ "_" @ %sd;
	
	return %correcttimestr;
}

////////////////////////////////////////////////////////
//Backups

function RedAutosave_BackupLastAutosave() {
	%savefilepath = "saves/" @ $Pref::Server::RedAutosave::SaveFile @ ".bls";
	
	%file = new FileObject();
	%opened = %file.openForRead(%savefilepath);
	if(!%opened) { warn("RedAutosave: No save to back up."); %file.delete(); return; }
	%savetime = "";
	while(!%file.isEOF() && %savetime$="") {
		%line = %file.readLine();
		if(getSubStr(%line, 0, 13)$="Autosaved at ") {
			%savetime = getSubStr(%line, 13, 17);
		}
	}
	%file.close();
	%file.delete();
	
	if(%savetime$="") {
		warn("RedAutosave: Backup save does not have timestamp.");
		return;
	}
	
	%file = new FileObject();
	%opened = %file.openForWrite("saves/"@ $Pref::Server::RedAutosave::BackupDir @ "/dummy.txt");
	if(!%opened) { warn("RedAutosave: Failed to create autosave backup directory"); %file.delete(); return; }
	%file.writeLine("Apparently fileCopy doesn't create directories, and silently does nothing if the destination doesn't exist. Torque moment.");
	%file.close(); %file.delete();
	
	%backupfilename = strReplace($Pref::Server::RedAutosave::BackupFile, "%T", %savetime);
	
	%backupfilepath = "saves/" @ $Pref::Server::RedAutosave::BackupDir @ "/" @ %backupfilename @ ".bls";
	
	fileCopy(%savefilepath, %backupfilepath);
	
	%file = new FileObject();
	%opened = %file.openForRead(%backupfilepath);
	if(!%opened) { warn("RedAutosave: Failed to copy last save to backup folder"); %file.delete(); return; }
	%file.close(); %file.delete();
	
	echo("RedAutosave: Backed up to " @ %backupfilepath @ "");
}

function RedAutosave_Autosave() {
	%bricks = RedAutosave_GetBrickCount();
	
	if(%bricks==0) {
		echo("RedAutosave: Did not save due to lack of bricks.");
		messageAll('', "\c6Did not autosave due to lack of bricks.");
		return;
	}
	
	$RedAutosave::SavesSinceLastBackup++;
	if($RedAutosave::SavesSinceLastBackup>=$Pref::Server::RedAutosave::BackupInterval) {
		$RedAutosave::SavesSinceLastBackup = 0;
		RedAutosave_BackupLastAutosave();
		%backup = true;
	}
	
	fileDelete("saves/" @ $Pref::Server::RedAutosave::SaveFile @ ".bls");
	%bricksSaved = RedAutosave_SaveBricksToFile(fileBase($Pref::Server::RedAutosave::SaveFile), !!$Pref::Server::RedAutosave::SaveEvents, !!$Pref::Server::RedAutosave::SaveOwnership, 1);
	
	if(isFunction(lualogic_savedata)) {
		lualogic_savedata();
	}
	
	if(%bricksSaved>0) {
		messageAll('', "\c6Autosaved \c3" @ %bricksSaved @ "\c6 bricks" @ (%backup ? " and backed up previous save." : "."));
	}
}

////////////////////////////////////////////////////////
//Saving
//pretty much copied from zack0's autosaver

function RedAutosave_SaveBricksToFile(%filename, %events, %ownership, %overwrite) {
	if(%filename $= "" || %events $= "" || %ownership $= "" || (%events !$= 1 && %events !$= 0) || (%ownership !$= 1 && %ownership !$= 0)) {
		warn("RedAutosave: Arguments incorrect.");
		return;
	}
	
	%filepath = "saves/" @ %filename @ ".bls";
	
	//if(!isWriteableFileName(%filepath)) {
	//	warn("RedAutosave: File " @ %filepath @ " is not writeable.");
	//	return;
	//}
	
	if(isFile(%filepath) && !%overwrite) {
		warn("RedAutosave: File " @ %filepath @ " already exists.");
		return;
	}
	
	%timestring = RedAutosave_GetCorrectTimeString();
	
	%file = new FileObject();
	
	%file.openForWrite(%filepath);
	%file.writeLine("This is a Blockland save file.  You probably shouldn't modify it cause you'll screw it up.");
	%file.writeLine("1");
	%file.writeLine("Autosaved at " @ %timestring @ ".");
	
	for(%i=0;%i<64;%i++) {
		%file.writeLine(getColorIDTable(%i));
	}
	
	//%bricks = RedAutosave_GetBrickCount();
	//%file.writeLine("Linecount " @ %bricks);
	%file.writeLine("Linecount 0");
	
	%brickssaved = 0;
	
	for(%i=0;%i<mainBrickGroup.getCount();%i++) {
		%group = mainBrickGroup.getObject(%i);
		
		for(%a=0;%a<%group.getCount();%a++) {
			%brick = %group.getObject(%a);
			
			if(%brick.generated) { continue; }
			
			%print = "";
			if(%brick.getDataBlock().hasPrint) {
				%texture = getPrintTexture(%brick.getPrintId());
				%printPath = filePath(%texture);
				%underscorePos = strPos(%printPath, "_");
				%name = getSubStr(%printPath, %underscorePos + 1, strPos(%printPath, "_", 14) - 14) @ "/" @ fileBase(%texture);
				
				if($printNameTable[%name] !$= "")
					%print = %name;
			}
			
			%file.writeLine(%brick.getDataBlock().uiName @ "\" " @ %brick.getPosition() SPC %brick.getAngleID() SPC %brick.isBasePlate() SPC %brick.getColorID() SPC %print SPC %brick.getColorFXID() SPC %brick.getShapeFXID() SPC %brick.isRayCasting() SPC %brick.isColliding() SPC %brick.isRendering());
			
			if(%ownership && !$Server::LAN)
				%file.writeLine("+-OWNER " @ getBrickGroupFromObject(%brick).bl_id);
			
			if(%events) {
				if(%brick.getName() !$= "")
					%file.writeLine("+-NTOBJECTNAME " @ %brick.getName());
				
				for(%b=0;%b<%brick.numEvents;%b++) {
					%targetClass = %brick.eventTargetIdx[%b] >= 0 ? getWord(getField($InputEvent_TargetListfxDTSBrick_[%brick.eventInputIdx[%b]], %brick.eventTargetIdx[%b]), 1) : "fxDtsBrick";
					%paramList = $OutputEvent_parameterList[%targetClass, %brick.eventOutputIdx[%b]];
					%params = "";
					
					for(%c=0;%c<4;%c++) {
						if(firstWord(getField(%paramList, %c)) $= "dataBlock" && isObject(%brick.eventOutputParameter[%b, %c + 1]))
							%params = %params TAB %brick.eventOutputParameter[%b, %c + 1].getName();
						else
							%params = %params TAB %brick.eventOutputParameter[%b, %c + 1];
					}
					
					%file.writeLine("+-EVENT" TAB %b TAB %brick.eventEnabled[%b] TAB %brick.eventInput[%b] TAB %brick.eventDelay[%b] TAB %brick.eventTarget[%b] TAB %brick.eventNT[%b] TAB %brick.eventOutput[%b] @ %params);
				}
			}
			
			if(isObject(%brick.emitter))
				%file.writeLine("+-EMITTER " @ %brick.emitter.emitter.uiName @ "\" " @ %brick.emitterDirection);
			if(%brick.getLightID() >= 0)
				%file.writeLine("+-LIGHT " @ %brick.getLightID().getDataBlock().uiName @ "\" "); // Not sure if something else comes after the name
			if(isObject(%brick.item))
				%file.writeLine("+-ITEM " @ %brick.item.getDataBlock().uiName @ "\" " @ %brick.itemPosition SPC %brick.itemDirection SPC %brick.itemRespawnTime);
			if(isObject(%brick.audioEmitter))
				%file.writeLine("+-AUDIOEMITTER " @ %brick.audioEmitter.getProfileID().uiName @ "\" "); // Not sure if something else comes after the name
			if(isObject(%brick.vehicleSpawnMarker))
				%file.writeLine("+-VEHICLE " @ %brick.vehicleSpawnMarker.uiName @ "\" " @ %brick.reColorVehicle);
			
			%brickssaved++;
		}
	}
	
	%file.close();
	%file.delete();
	
	echo("RedAutosave: Saved " @ %bricks @ " bricks to " @ %filepath @ "");
	
	return %brickssaved;
}

////////////////////////////////////////////////////////
//Loading

function RedAutosave_CreateHostBrickGroup() {
	%blid = getMyBLID();
	%bg = "BrickGroup_" @ %blid;
	if(!isObject(%bg)) {
		%bg = new SimGroup(%bg) {
			client = 0;
			name = "\c2BL_ID: " @ %blid @ "\c2\c1";
			bl_id = %blid;
		};
		MainBrickGroup.add(%bg);
	}
}

function RedAutosave_LoadBricksFromFile(%filename) {
	RedAutosave_CreateHostBrickGroup();
	if(isFunction(serverDirectSaveFileLoad2)) {
		serverDirectSaveFileLoad2(%filename, 3);
	} else {
		serverDirectSaveFileLoad (%filename, 3);
	}
}

function RedAutosave_LoadLatestAutosave() {
	RedAutosave_LoadBricksFromFile("saves/Autosave_Latest.bls");
}

////////////////////////////////////////////////////////
//Operation

function RedAutosave_Start() {
	if(!$RedAutosave::Running) {
		$RedAutosave::Running = true;
		$Pref::Server::RedAutosave::Running = true;
		
		RedAutosave_Bomb.start();
		
		echo("RedAutosave: Started");
	}
}

function RedAutosave_Stop() {
	if($RedAutosave::Running) {
		$RedAutosave::Running = false;
		$Pref::Server::RedAutosave::Running = false;
		
		RedAutosave_Bomb.end();
		
		echo("RedAutosave: Stopped");
	}
}

function RedAutosave_SetSaveTime(%time) {
	$Pref::Server::RedAutosave::SaveTime = %time;
	
	if($RedAutosave::Running) {
		RedAutosave_Stop();
		%wasrunning = true;
	}
	
	RedAutosave_Bomb.requiredTicks = %time;
	
	if(%wasrunning) {
		RedAutosave_Start();
	}
	
	echo("RedAutosave: Save time set to " @ %time @ " minutes");
}

function RedAutosave_ResetTimer() {
	if($RedAutosave::Running) {
		RedAutosave_Stop();
		RedAutosave_Start();
	}
}

function RedAutosave_SetBackupInterval(%interval) {
	$Pref::Server::RedAutosave::BackupInterval = %interval;
}

////////////////////////////////////////////////////////
//Commands

function serverCmdRASStart(%client) {
	if(%client.isSuperAdmin) {
		messageAll('', "\c3" @ %client.name @ "\c6 started the auto saver.");
		RedAutosave_Start();
	}
}

function serverCmdRASStop(%client) {
	if(%client.isSuperAdmin) {
		messageAll('', "\c3" @ %client.name @ "\c6 stopped the auto saver.");
		RedAutosave_Stop();
	}
}

function serverCmdRASSave(%client, %b, %c, %d, %e, %f, %g, %h, %i, %j, %k, %l, %m, %n, %o, %p) {
	%filename = trim(%b SPC %c SPC %d SPC %e SPC %f SPC %g SPC %h SPC %i SPC %j SPC %k SPC %l SPC %m SPC %n SPC %o SPC %p);
	if(%client.isSuperAdmin) {
		if(%filename $= "") {
			messageAll('', "\c3" @ %client.name @ "\c6 forced an autosave.");
			RedAutosave_Autosave();
			RedAutosave_ResetTimer();
		} else {
			%fullfilename = "saves/" @ %filename @ ".bls";
			if(isFile(%fullfilename) && !(%client.RAS_lastAttemptedOverwriteFile $= %fullfilename && %client.RAS_lastAttemptedOverwriteTime >= getSimTime() - 10000)) { // If client is repeating save command within 10 seconds of last attempt
				messageClient(%client, '', "\c6A save file called \c3\"" @ %fullfilename @ "\"\c6 already exists. Repeat the command to overwrite it.");
				%client.RAS_lastAttemptedOverwriteFile = %fullfilename;
				%client.RAS_lastAttemptedOverwriteTime = getSimTime();
			} else {
				messageAll('', "\c3" @ %client.name @ "\c6 saved bricks to \c3\"" @ %filename @ "\"");
				%bricksSaved = RedAutosave_SaveBricksToFile(%filename, !!$Pref::Server::RedAutosave::SaveEvents, !!$Pref::Server::RedAutosave::SaveOwnership, 1);
				if(%bricksSaved > 0) {
					messageAll('', "\c6Saved \c3" @ %bricksSaved @ "\c6 bricks.");
				}
			}
		}
	}
}

function serverCmdRASLoad(%client, %b, %c, %d, %e, %f, %g, %h, %i, %j, %k, %l, %m, %n, %o, %p) {
	%filename = trim(%b SPC %c SPC %d SPC %e SPC %f SPC %g SPC %h SPC %i SPC %j SPC %k SPC %l SPC %m SPC %n SPC %o SPC %p);
	if(%client.isSuperAdmin) {
		if(%filename $= "") {
			messageAll('', "\c3" @ %client.name @ "\c6 loaded the latest autosave.");
			RedAutosave_LoadLatestAutosave();
		} else {
			%fullfilename = "saves/" @ %filename @ ".bls";
			if(!isFile(%fullfilename)) {
				messageClient(%client, '', "\c6No save file called \c3\"" @ %fullfilename @ "\"\c6 exists.");
			} else {
				messageAll('', "\c3" @ %client.name @ "\c6 loaded save \c3\"" @ %filename @ "\"\c6.");
				RedAutosave_LoadBricksFromFile(%fullfilename);
			}
		}
	}
}

function serverCmdRASTime(%client, %time) {
	if(%client.isSuperAdmin) {
		%time = mFloor(%time);
		%time = mClamp(%time, 1, 1440);
		messageAll('', "\c3" @ %client.name @ "\c6 set the autosave time to \c3" @ %time @ "\c6 minute" @ (%time==1 ? "" : "s") @ ".");
		RedAutosave_SetSaveTime(%time);
	}
}

function serverCmdRASInterval(%client, %interval) {
	if(%client.isSuperAdmin) {
		%interval = mFloor(%interval);
		%interval = mClamp(%interval, 1, 1000);
		messageAll('', "\c3" @ %client.name @ "\c6 set the autosave backup interval to \c3" @ %interval @ "\c6 save" @ (%interval==1 ? "" : "s") @ ".");
		RedAutosave_SetBackupInterval(%interval);
	}
}

////////////////////////////////////////////////////////
//Init

function RedAutosave_Init() {
	if($Pref::Server::RedAutosave::Running && !$RedAutosave::Running) {
		RedAutosave_Start();
	}
	if($Pref::Server::RedAutosave::LoadOnStart) {
		RedAutosave_LoadLatestAutosave();
	}
}
schedule(30000, 0, RedAutosave_Init);
